using System.Collections.Generic;
using System.Threading.Tasks;
using FastTests;
using FastTests.Utils;
using Raven.Client.Documents.Operations.CompareExchange;
using Raven.Server.ServerWide.Context;
using Raven.Server.ServerWide.Maintenance;
using Tests.Infrastructure;
using Xunit;
using Xunit.Abstractions;

namespace SlowTests.Issues;

public class RavenDB_20206 : RavenTestBase
{
    public RavenDB_20206(ITestOutputHelper output) : base(output)
    {
    }

    [RavenFact(RavenTestCategory.Cluster)]
    public async Task Should_Not_Clear_Compare_Exchange_Tombstones_Of_A_Database_With_Identical_Prefix()
    {
        using (var server = GetNewServer())
        using (var store1 = GetDocumentStore(new Options { Server = server }))
        using (var store2 = GetDocumentStore(new Options { Server = server, ModifyDatabaseName = _ => store1.Database + "_123" }))
        {
            Cluster.WaitForFirstCompareExchangeTombstonesClean(server);
            var indexesList1 = new Dictionary<string, long>();
            var indexesList2 = new Dictionary<string, long>();
            // create 3 unique values
            for (int i = 0; i < 3; i++)
            {
                var res1 = await store1.Operations.SendAsync(new PutCompareExchangeValueOperation<int>($"{i}", i, 0));
                var res2 = await store2.Operations.SendAsync(new PutCompareExchangeValueOperation<int>($"{i}", i, 0));
                indexesList1.Add($"{i}", res1.Index);
                indexesList2.Add($"{i}", res2.Index);
            }

            // delete 1 unique value
            var del1 = await store1.Operations.SendAsync(new DeleteCompareExchangeValueOperation<int>("2", indexesList1["2"]));
            Assert.NotNull(del1.Value);
            indexesList1.Remove("2");

            var del2 = await store2.Operations.SendAsync(new DeleteCompareExchangeValueOperation<int>("2", indexesList2["2"]));
            Assert.NotNull(del2.Value);
            indexesList2.Remove("2");

            using (server.ServerStore.Engine.ContextPool.AllocateOperationContext(out ClusterOperationContext context))
            using (context.OpenReadTransaction())
            {
                var numOfCompareExchangeTombstones = server.ServerStore.Cluster.GetNumberOfCompareExchangeTombstones(context, store1.Database);
                Assert.Equal(1, numOfCompareExchangeTombstones);
            }

            using (server.ServerStore.Engine.ContextPool.AllocateOperationContext(out ClusterOperationContext context))
            using (context.OpenReadTransaction())
            {
                var numOfCompareExchangeTombstones = server.ServerStore.Cluster.GetNumberOfCompareExchangeTombstones(context, store2.Database);
                Assert.Equal(1, numOfCompareExchangeTombstones);
            }

                // clean tombstones
                var cleanupState1 = await CompareExchangeTombstoneCleanerTestHelper.Clean(nodes: [server], store1.Database, ignoreClustrTrx: true);
                Assert.Equal(ClusterObserver.CompareExchangeTombstonesCleanupState.NoMoreTombstones, cleanupState1);

            using (server.ServerStore.Engine.ContextPool.AllocateOperationContext(out ClusterOperationContext context))
            using (context.OpenReadTransaction())
            {
                var numOfCompareExchangeTombstones = server.ServerStore.Cluster.GetNumberOfCompareExchangeTombstones(context, store1.Database);
                var numOfCompareExchanges = server.ServerStore.Cluster.GetNumberOfCompareExchange(context, store1.Database);
                Assert.Equal(0, numOfCompareExchangeTombstones);
                Assert.Equal(2, numOfCompareExchanges);

                numOfCompareExchangeTombstones = server.ServerStore.Cluster.GetNumberOfCompareExchangeTombstones(context, store2.Database);
                numOfCompareExchanges = server.ServerStore.Cluster.GetNumberOfCompareExchange(context, store2.Database);
                Assert.Equal(1, numOfCompareExchangeTombstones);
                Assert.Equal(2, numOfCompareExchanges);
            }

            // clean tombstones
            var cleanupState = await CompareExchangeTombstoneCleanerTestHelper.Clean(nodes: [server], store2.Database,ignoreClustrTrx: true);
            Assert.Equal(ClusterObserver.CompareExchangeTombstonesCleanupState.NoMoreTombstones, cleanupState);

            using (server.ServerStore.Engine.ContextPool.AllocateOperationContext(out ClusterOperationContext context))
            using (context.OpenReadTransaction())
            {
                var numOfCompareExchangeTombstones = server.ServerStore.Cluster.GetNumberOfCompareExchangeTombstones(context, store1.Database);
                var numOfCompareExchanges = server.ServerStore.Cluster.GetNumberOfCompareExchange(context, store1.Database);
                Assert.Equal(0, numOfCompareExchangeTombstones);
                Assert.Equal(2, numOfCompareExchanges);

                numOfCompareExchangeTombstones = server.ServerStore.Cluster.GetNumberOfCompareExchangeTombstones(context, store2.Database);
                numOfCompareExchanges = server.ServerStore.Cluster.GetNumberOfCompareExchange(context, store2.Database);
                Assert.Equal(0, numOfCompareExchangeTombstones);
                Assert.Equal(2, numOfCompareExchanges);
            }
        }
    }
}
